package com.lb.uts.util;

import com.lb.uts.exception.RscException;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpMessage;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.UnsupportedCharsetException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 远程服务调用工具--HTTP方式
 *
 * @author liangb
 *
 */
public class RscHttpUtils {

    private static CloseableHttpClient httpclient = createSSLClientDefault();

    private static final String defaultCharset = "UTF-8";

    public static CloseableHttpClient createSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                //信任所有
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            return HttpClients.custom().setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)).build();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();
    }

    /**
     * POST方式发起远程调用
     * @param url 调用地址
     * @param headers 请求头
     * @param params 请求参数
     * @param timeOutMillis 超时时间（毫秒）
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String post(String url, Map<String, String> headers, Map<String, String> params, Integer timeOutMillis, String charset) {
        HttpPost post = new HttpPost(url);

        if (StringUtils.isEmpty(charset)) {
            charset = defaultCharset;
        }

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if (params != null) {
            for (Entry<String, String> entry : params.entrySet()) {
                nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
        }

        //添加请求头
        addHeader(post, headers);

        //设置超时时间
        setConfig(post, timeOutMillis);

        try {
            post.setEntity(new UrlEncodedFormEntity(nvps, charset));
        } catch (UnsupportedEncodingException e) {
            throw new RscException(e);
        }

        return invoke(post, charset);
    }

    /**
     * POST方式发起远程调用
     * @param url 调用地址
     * @param headers 请求头
     * @param body 直接的请求内容（非键值对），例如：JSON串
     * @param timeOutMillis 超时时间（毫秒）
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String post(String url, Map<String, String> headers, String body, Integer timeOutMillis, String charset) {
        HttpPost post = new HttpPost(url);

        if (StringUtils.isEmpty(charset)) {
            charset = defaultCharset;
        }

        //添加请求头
        addHeader(post, headers);

        //设置超时时间
        setConfig(post, timeOutMillis);

        try {
            StringEntity stringEntity = new StringEntity(body, charset);
            stringEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING, "UTF-8"));
            post.setEntity(stringEntity);
        } catch (UnsupportedCharsetException e) {
            throw new RscException(e);
        }

        return invoke(post, charset);
    }

    /**
     * POST方式发起远程调用，默认为UTF-8编码
     * @param url 调用地址
     * @param params 请求参数
     * @param timeOutMillis 超时时间（毫秒）
     * @return 返回报文
     * @throws RscException
     */
    public static String post(String url, Map<String, String> params, Integer timeOutMillis) {
        return post(url, null, params, timeOutMillis, defaultCharset);
    }

    /**
     * POST方式发起远程调用
     * @param url 调用地址
     * @param params 请求参数
     * @param timeOutMillis 超时时间（毫秒）
     * @param charset
     * @return 返回报文
     * @throws RscException
     */
    public static String post(String url, Map<String, String> params, Integer timeOutMillis, String charset) {
        return post(url, null, params, timeOutMillis, charset);
    }

    /**
     * POST方式发起远程调用，默认为UTF-8编码
     * @param url 调用地址
     * @param params 请求参数
     * @return 返回报文
     * @throws RscException
     */
    public static String post(String url, Map<String, String> params) {
        return post(url, null, params, null, defaultCharset);
    }

    /**
     * POST方式发起远程调用
     * @param url 调用地址
     * @param params 请求参数
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String post(String url, Map<String, String> params, String charset) {
        return post(url, null, params, null, charset);
    }

    /**
     * GET方式发起远程调用
     * @param url 调用地址，参数直接在url中带入
     * @param headers 请求头
     * @param timeOutMillis 超时时间（毫秒）
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Map<String, String> headers, Integer timeOutMillis, String charset) {
        if (StringUtils.isEmpty(charset)) {
            charset = defaultCharset;
        }

        HttpGet httpGet = new HttpGet(url);

        //添加请求头
        addHeader(httpGet, headers);

        //设置超时时间
        setConfig(httpGet, timeOutMillis);

        return invoke(httpGet, charset);
    }

    /**
     * GET方式发起远程调用
     * @param url 调用地址，不带参数
     * @param headers 请求头
     * @param params 请求参数
     * @param timeOutMillis 超时时间（毫秒）
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Map<String, String> headers, Map<String, String> params, Integer timeOutMillis, String charset) {
        if (StringUtils.isEmpty(charset)) {
            charset = defaultCharset;
        }
        if (params != null && !params.isEmpty()) {
            List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
            for (Entry<String, String> entry : params.entrySet()) {
                String value = entry.getValue();
                if (StringUtils.isNotEmpty(value)) {
                    pairs.add(new BasicNameValuePair(entry.getKey(), value));
                }
            }
            try {
                url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset), charset);
            } catch (IOException e) {
                throw new RscException(e);
            }
        }

        return get(url, headers, timeOutMillis, charset);
    }

    /**
     * GET方式发起远程调用
     * @param url 调用地址，不带参数
     * @param params 请求参数
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Map<String, String> params, String charset) {
        return get(url, null, params, null, charset);
    }

    /**
     * GET方式发起远程调用，默认为UTF-8编码
     * @param url 调用地址，不带参数
     * @param params 请求参数
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Map<String, String> params) {
        return get(url, null, params, null, defaultCharset);
    }

    /**
     * GET方式发起远程调用，默认为UTF-8编码
     * @param url 调用地址，参数直接在url中带入
     * @param headers 请求头
     * @param timeOutMillis 超时时间（毫秒）
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Map<String, String> headers, Integer timeOutMillis) {
        return get(url, null, timeOutMillis, defaultCharset);
    }

    /**
     * GET方式发起远程调用，默认为UTF-8编码
     * @param url 调用地址，参数直接在url中带入
     * @param timeOutMillis 超时时间（毫秒）
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Integer timeOutMillis) {
        return get(url, null, timeOutMillis, defaultCharset);
    }

    /**
     * GET方式发起远程调用
     * @param url 调用地址，参数直接在url中带入
     * @param timeOutMillis 超时时间（毫秒）
     * @param charset 字符编码
     * @return 返回报文
     * @throws RscException
     */
    public static String get(String url, Integer timeOutMillis, String charset) {
        return get(url, null, timeOutMillis, charset);
    }

    /**
     * 配置连接属性
     * @param request
     */
    private static void setConfig(HttpRequestBase request, Integer timeOutMillis) {
        Builder builder = RequestConfig.custom();

        //超时时间
        if (timeOutMillis != null) {
            builder.setConnectTimeout(timeOutMillis);
            builder.setSocketTimeout(timeOutMillis);
            builder.setConnectionRequestTimeout(timeOutMillis);
        }

        request.setConfig(builder.build());
    }

    /**
     * 添加请求头
     * @param request
     * @param headers
     */
    private static void addHeader(HttpMessage request, Map<String, String> headers) {
        if (headers != null) {
            for (Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 发起请求
     * @param request
     * @return 返回报文
     */
    private static String invoke(HttpUriRequest request, String charset) {
        String body = null;
        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(request);

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                request.abort();
                throw new RscException("响应状态异常，状态值：" + statusCode);
            }

            body = RscResponseUtils.parseResponse(response, charset);
        } catch (IOException e) {
            throw new RscException(e);
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    throw new RscException(e);
                }
            }
        }
        return body;
    }

}
